/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * LocalValidationContext.java
 * 
 *                                   Revision History
 * -----------------------------------------------------------------------------------------
 * Author                    Date            CR        Comments
 * ----------------------|------------|--------------|--------------------------------------
 * pcmnac                  2007.10.27                  Initial version.
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package com.google.code.localvalidator;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * <p>
 * Represents a validation context.
 * </p>
 * <p>
 * Once created, several validation cases can be added to context. These validations will be performed just after
 * the {@link #validate()} method be called.
 * </p>
 * 
 * @author pcmnac@gmail.com
 * 
 */
public class LocalValidationContext
{
  // ===========================================================================================================
  // Public API.
  // ===========================================================================================================

  /**
   * Default constructor.
   */
  public LocalValidationContext()
  {
    this(DEFAULT_EXCEPTION_CLASS);
  }

  /**
   * Constructor.
   * 
   * @param exceptionClass
   *          Class of exception to be thrown when a validation error occurs.
   */
  public LocalValidationContext(Class<? extends RuntimeException> exceptionClass)
  {
    this.exceptionClass = exceptionClass;
  }

  /**
   * Validates all cases present in {@link #validationCases} list and throws appropriate exceptions.
   * 
   */
  public LocalValidationContext validate()
  {
    LocalValidatorUtil.validate(validationCases);
    return this;
  }

  /**
   * Validates the received condition and throws an exception of given class.
   * 
   * @param validContition
   *          The valid condition.
   * @param exceptionClass
   *          The class of exception to be thrown.
   * @param exceptionArguments
   *          The exception arguments.
   */
  public static void validate(boolean validContition, Class<? extends RuntimeException> exceptionClass,
      Object... exceptionArguments)
  {
    LocalValidatorUtil.validate(new LocalValidationCase[] { new LocalValidationCase(validContition,
        exceptionClass, exceptionArguments) });
  }

  /**
   * Forwards the request to {@link #validate(boolean, Class, Object...)} passing a <code>null</code>
   * exceptionArguments.
   */
  public static void validate(boolean validContition, Class<? extends RuntimeException> exceptionClass)
  {
    LocalValidatorUtil.validate(new LocalValidationCase[] { new LocalValidationCase(validContition,
        exceptionClass) });
  }

  /**
   * Validates the received condition and throws an exception of given class.
   * 
   * @param validContition
   *          The valid condition.
   * @param exception
   *          The exception to be thrown.
   */
  public static void validate(boolean validContition, RuntimeException exception)
  {
    LocalValidatorUtil
        .validate(new LocalValidationCase[] { new LocalValidationCase(validContition, exception) });
  }

  /**
   * Clears the {@link #validationCases} list.
   * 
   */
  public LocalValidationContext clear()
  {
    validationCases.clear();
    exceptionClass = DEFAULT_EXCEPTION_CLASS;
    return this;
  }

  /**
   * Retrieves the value of {@link #exceptionClass}.
   * 
   * @return The value of {@link #exceptionClass}.
   */
  public Class<? extends RuntimeException> getExceptionClass()
  {
    return exceptionClass;
  }

  /**
   * Sets the value of {@link #exceptionClass}.
   * 
   * @param exceptionClass
   *          New value to {@link #exceptionClass}.
   * @return Itself reference.
   */
  public LocalValidationContext setExceptionClass(Class<? extends RuntimeException> exceptionClass)
  {
    this.exceptionClass = exceptionClass;
    return this;
  }

  //
  // VALID CONDITION...
  //

  /**
   * Adds a valid condition to be checked. If this condition fails an exception will be thrown.
   * 
   * @param condition
   *          The valid condition to be checked.
   * @param exception
   *          The exception to be thrown.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addValidCondition(boolean condition, RuntimeException exception)
  {
    validationCases.add(new LocalValidationCase(condition, exception));
    return this;
  }

  /**
   * Adds a valid condition to be checked. If this condition fails an exception will be thrown.
   * 
   * @param condition
   *          The valid condition to be checked.
   * @param exceptionClass
   *          The exception class to be thrown.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addValidCondition(boolean condition,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    validationCases.add(new LocalValidationCase(condition, exceptionClass, exceptionArguments));
    return this;
  }

  /**
   * Forwards the request to {@link #addValidCondition(boolean, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addValidCondition(boolean condition, String exceptionMessage)
  {
    addValidCondition(condition, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addValidCondition(boolean, Class, Object...)} passing a <code>null</code>
   * exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addValidCondition(boolean condition)
  {
    addValidCondition(condition, exceptionClass);
    return this;
  }

  //
  // INVALID CONDITION...
  //

  /**
   * Adds a invalid condition to be checked. If this condition doesn't fail an exception will be thrown.
   * 
   * @param condition
   *          The valid condition to be checked.
   * @param exception
   *          The exception to be thrown.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addInvalidCondition(boolean condition, RuntimeException exception)
  {
    validationCases.add(new LocalValidationCase(!condition, exception));
    return this;
  }

  /**
   * Adds a invalid condition to be checked. If this condition doesn't fail an exception will be thrown.
   * 
   * @param condition
   *          The invalid condition to be checked.
   * @param exceptionClass
   *          The exception class to be thrown.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addInvalidCondition(boolean condition,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    validationCases.add(new LocalValidationCase(!condition, exceptionClass, exceptionArguments));
    return this;
  }

  /**
   * Forwards the request to {@link #addInvalidCondition(boolean, Class, Object...)} passing
   * {@link #exceptionClass} .
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addInvalidCondition(boolean condition, String exceptionMessage)
  {
    addInvalidCondition(condition, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addInvalidCondition(boolean, Class, Object...)} passing a <code>null</code>
   * exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addInvalidCondition(boolean condition)
  {
    addInvalidCondition(condition, exceptionClass);
    return this;
  }

  //
  // NOT NULL CHECKING...
  //

  /**
   * Adds NOT NULL checking for a given object.
   * 
   * @param object
   *          The object to be validated.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotNullChecking(Object object, RuntimeException exception)
  {
    addValidCondition(object != null, exception);
    return this;
  }

  /**
   * Adds NOT NULL checking for a given object.
   * 
   * @param object
   *          The object to be validated.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotNullChecking(Object object,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(object != null, exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotNullChecking(Object, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotNullChecking(Object object, String exceptionMessage)
  {
    addNotNullChecking(object, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotNullChecking(Object, Class, Object...)} passing a <code>null</code>
   * exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotNullChecking(Object object)
  {
    addNotNullChecking(object, exceptionClass);
    return this;
  }

  //
  // NULL CHECKING...
  //

  /**
   * Adds NULL checking for a given object.
   * 
   * @param object
   *          The object to be validated.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNullChecking(Object object, RuntimeException exception)
  {
    addValidCondition(object == null, exception);
    return this;
  }

  /**
   * Adds NULL checking for a given object.
   * 
   * @param object
   *          The object to be validated.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNullChecking(Object object, Class<? extends RuntimeException> exceptionClass,
      Object... exceptionArguments)
  {
    addValidCondition(object == null, exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addNullChecking(Object, Class, Object...)} passing {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNullChecking(Object object, String exceptionMessage)
  {
    addNullChecking(object, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addNullChecking(Object, Class, Object...)} passing a <code>null</code>
   * exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNullChecking(Object object)
  {
    addNullChecking(object, exceptionClass);
    return this;
  }

  //
  // NOT EMPTY CHECKING...
  //

  /**
   * Adds the empty checking for a given {@link String}. It validates if the given {@link String} is NOT NULL and
   * has any content.
   * 
   * @param string
   *          The {@link String} to be validated.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEmptyChecking(String string, RuntimeException exception)
  {
    addValidCondition(string != null && string.trim().length() > 0, exception);
    return this;
  }

  /**
   * Adds the empty checking for a given {@link String}. It validates if the given {@link String} is NOT NULL and
   * has any content.
   * 
   * @param string
   *          The {@link String} to be validated.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEmptyChecking(String string,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(string != null && string.trim().length() > 0, exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotEmptyChecking(String, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEmptyChecking(String string, String exceptionMessage)
  {
    addNotEmptyChecking(string, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotEmptyChecking(String, Class, Object...)} passing a <code>null</code>
   * exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEmptyChecking(String string)
  {
    addNotEmptyChecking(string, exceptionClass);
    return this;
  }

  //
  // EQUALS CHECKING...
  //

  /**
   * Adds the equals checking for a given object pair. It validates if the given objects are equals through the
   * equals method.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addEqualsChecking(Object object1, Object object2, RuntimeException exception)
  {
    addValidCondition(object1 != null && object1.equals(object2), exception);
    return this;
  }

  /**
   * Adds the equals checking for a given object pair. It validates if the given objects are equals through the
   * equals method.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addEqualsChecking(Object object1, Object object2,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(object1 != null && object1.equals(object2), exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addEqualsChecking(Object, Object, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addEqualsChecking(Object object1, Object object2, String exceptionMessage)
  {
    addEqualsChecking(object1, object2, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addEqualsChecking(Object, Object, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addEqualsChecking(Object object1, Object object2)
  {
    addEqualsChecking(object1, object2, exceptionClass);
    return this;
  }

  //
  // NOT EQUALS CHECKING...
  //

  /**
   * Adds the NOT equals checking for a given object pair. It validates if the given objects are not equals
   * through the equals method.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEqualsChecking(Object object1, Object object2, RuntimeException exception)
  {
    addValidCondition(!object1.equals(object2), exception);
    return this;
  }

  /**
   * Adds the NOT equals checking for a given object pair. It validates if the given objects are not equals
   * through the equals method.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEqualsChecking(Object object1, Object object2,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(!object1.equals(object2), exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotEqualsChecking(Object, Object, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEqualsChecking(Object object1, Object object2, String exceptionMessage)
  {
    addNotEqualsChecking(object1, object2, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotEqualsChecking(Object, Object, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotEqualsChecking(Object object1, Object object2)
  {
    addNotEqualsChecking(object1, object2, exceptionClass);
    return this;
  }

  //
  // SAME CHECKING...
  //

  /**
   * Adds the SAME checking for a given object pair. It validates if the given objects are the same reference.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addSameChecking(Object object1, Object object2, RuntimeException exception)
  {
    addValidCondition(object1 == object2, exception);
    return this;
  }

  /**
   * Adds the SAME checking for a given object pair. It validates if the given objects are the same reference.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addSameChecking(Object object1, Object object2,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(object1 == object2, exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addSameChecking(Object, Object, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addSameChecking(Object object1, Object object2, String exceptionMessage)
  {
    addSameChecking(object1, object2, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addSameChecking(Object, Object, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addSameChecking(Object object1, Object object2)
  {
    addSameChecking(object1, object2, exceptionClass);
    return this;
  }

  //
  // NOT SAME CHECKING...
  //

  /**
   * Adds the NOT SAME checking for a given object pair. It validates if the given objects are not the same
   * reference.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotSameChecking(Object object1, Object object2, RuntimeException exception)
  {
    addValidCondition(object1 != object2, exception);
    return this;
  }

  /**
   * Adds the NOT SAME checking for a given object pair. It validates if the given objects are not the same
   * reference.
   * 
   * @param object1
   *          The first object.
   * @param object2
   *          The second object.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotSameChecking(Object object1, Object object2,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(object1 != object2, exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotSameChecking(Object, Object, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotSameChecking(Object object1, Object object2, String exceptionMessage)
  {
    addNotSameChecking(object1, object2, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addNotSameChecking(Object, Object, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addNotSameChecking(Object object1, Object object2)
  {
    addNotSameChecking(object1, object2, exceptionClass);
    return this;
  }

  //
  // REGEX MATCHES CHECKING...
  //

  /**
   * Adds the MATCHES checking for a given regex and text. It validates if the given regex matches the given
   * text.
   * 
   * @param regex
   *          The REGular EXpression.
   * @param text
   *          The text to be matched.
   * @param exception
   *          The exception.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addMatchingChecking(String regex, String text, RuntimeException exception)
  {
    addValidCondition(Pattern.matches(regex, text), exception);
    return this;
  }

  /**
   * Adds the MATCHES checking for a given regex and text. It validates if the given regex matches the given
   * text.
   * 
   * @param regex
   *          The REGular EXpression.
   * @param text
   *          The text to be matched.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addMatchingChecking(String regex, String text,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(Pattern.matches(regex, text), exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addMatchingChecking(String, String, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addMatchingChecking(String regex, String text, String exceptionMessage)
  {
    addMatchingChecking(regex, text, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addMatchingChecking(String, String, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addMatchingChecking(String regex, String text)
  {
    addMatchingChecking(regex, text, exceptionClass);
    return this;
  }

  //
  // CUSTOM VALIDATION...
  //

  /**
   * Adds a custom validation. It uses the {@link CustomValidation} interface.
   * 
   * @param validation
   *          The custom validation implementation.
   * @param exception
   *          The exception .
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addCustomValidation(CustomValidation validation, RuntimeException exception)
  {
    addValidCondition(validation.validate(), exception);
    return this;
  }

  /**
   * Adds a custom validation. It uses the {@link CustomValidation} interface.
   * 
   * @param validation
   *          The custom validator implementation.
   * @param exceptionClass
   *          The exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addCustomValidation(CustomValidation validation,
      Class<? extends RuntimeException> exceptionClass, Object... exceptionArguments)
  {
    addValidCondition(validation.validate(), exceptionClass, exceptionArguments);
    return this;
  }

  /**
   * Forwards the request to {@link #addCustomValidation(CustomValidation, Class, Object...)} passing
   * {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addCustomValidation(CustomValidation validation, String exceptionMessage)
  {
    addCustomValidation(validation, exceptionClass, exceptionMessage);
    return this;
  }

  /**
   * Forwards the request to {@link #addCustomValidation(CustomValidation, Class, Object...)} passing a
   * <code>null</code> exceptionArguments and {@link #exceptionClass}.
   * 
   * @return Itself reference.
   */
  public LocalValidationContext addCustomValidation(CustomValidation validation)
  {
    addCustomValidation(validation, exceptionClass);
    return this;
  }

  // ===========================================================================================================
  // Protected members.
  // ===========================================================================================================

  // ===========================================================================================================
  // Package members.
  // ===========================================================================================================

  // ===========================================================================================================
  // Private members.
  // ===========================================================================================================

  /**
   * The default exception class.
   */
  private static final Class<? extends RuntimeException> DEFAULT_EXCEPTION_CLASS = IllegalArgumentException.class;

  /**
   * The validation cases list.
   */
  private List<LocalValidationCase> validationCases = new ArrayList<LocalValidationCase>();

  /**
   * The exception class, used when an exception class is not specified by user.
   */
  private Class<? extends RuntimeException> exceptionClass;

}
